package com.sh.data.engine.domain.workspace.manager.job;

import com.google.common.collect.Lists;
import com.sh.data.engine.domain.base.model.enums.Database;
import com.sh.data.engine.domain.shims.DbManagerFactory;
import com.sh.data.engine.domain.shims.db.BaseDbManager;
import com.sh.data.engine.domain.shims.db.DbOptions;
import com.sh.data.engine.domain.workspace.manager.service.StorageService;
import com.sh.data.engine.job.core.handler.annotation.XxlJob;
import com.sh.data.engine.repository.dao.workspace.manager.entity.StorageEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author : mingxi.xpb
 * @date : 2022/1/14 10:46
 */
@Slf4j
@Component("storageCheckStatusTask")
public class StorageCheckStatusTask {

    @Autowired
    private StorageService storageService;

    @Autowired
    private StorageCheckConfig storageCheckConfig;

    @XxlJob("storageCheckStatusTaskHandler")
    public void storageCheckStatusTaskHandler() {
        if (!storageCheckConfig.isEnabled()) {
            log.warn("task checkStorageStatus is not enable, skip {}", this.getClass().getSimpleName());
            return;
        }

        List<StorageEntity> storageEntities = storageService.findStorageIsUsable();

        if (CollectionUtils.isEmpty(storageEntities)) {
            log.info("no storage need to check");
            return;
        }

        Map<String, List<StorageEntity>> collect =
            storageEntities.stream().collect(Collectors.groupingBy(StorageEntity::getStorageLink));

        long startCheckTime = System.currentTimeMillis();
        log.info(String.format("check start time: %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", new Date()));

        List<Long> succeedStorage = Lists.newCopyOnWriteArrayList();
        List<Long> failedStorage = Lists.newCopyOnWriteArrayList();
        for (Map.Entry<String, List<StorageEntity>> entry : collect.entrySet()) {
            StorageEntity storageEntity = entry.getValue().get(0);
            List<StorageEntity> storageEntityList = entry.getValue();
            Integer checkStatus = checkStatusOnce(storageEntity);
            if (checkStatus == 0) {
                failedStorage.addAll(
                    storageEntityList.stream().map(e -> e.getId()).collect(Collectors.toList()));
            } else {
                succeedStorage.addAll(
                    storageEntityList.stream().map(e -> e.getId()).collect(Collectors.toList()));
            }

            updateStorageStatus(succeedStorage, failedStorage);
        }

        log.info(
            "check storage status finished, size={}, cost={}ms, endTime{}",
            collect.size(),
            (System.currentTimeMillis() - startCheckTime),
            new Date());
    }

    private void updateStorageStatus(
        List<Long> succeedStorageLists, List<Long> failedStorageLists) {
        // update success
        if (CollectionUtils.isNotEmpty(succeedStorageLists)) {
            storageService.updateStatusByIds(1, succeedStorageLists);
        }
        // update failure
        if (CollectionUtils.isNotEmpty(failedStorageLists)) {
            storageService.updateStatusByIds(0, failedStorageLists);
        }
    }

    /**
     * description 对storageLink相同的存储只做一次检查
     *
     * @author : mingxi.xpb
     * @date : 2022/1/14 14:46
     */
    private Integer checkStatusOnce(StorageEntity storageEntity) {
        Long id = storageEntity.getId();
        String storageType = storageEntity.getStorageType();
        String storageLink = storageEntity.getStorageLink();

        BaseDbManager baseDbManager =
            getDbManager(
                storageEntity.getStorageType(),
                storageEntity.getRmdbs().getUsername(),
                storageEntity.getRmdbs().getPassword(),
                storageEntity.getStorageLink(),
                storageEntity.getRmdbs().getHiveSiteXmlPath());
        Integer status = null;
        try {
            baseDbManager.testConnection();
            status = 1;
        } catch (Exception e) {
            log.error(
                "update storage status error, id {} storageType {} storageLink {}",
                id,
                storageType,
                storageLink,
                e);
            status = 0;
        }
        return status;
    }

    /**
     * description 获取DbManager
     *
     * @author : mingxi.xpb
     * @date : 2022/1/14 11:25
     */
    private BaseDbManager getDbManager(
        String storageType,
        String username,
        String password,
        String storageLink,
        String hiveSiteXmlPath) {
        Database database = Database.from(storageType);
        DbOptions dbOptions = new DbOptions(database, username, password, storageLink, hiveSiteXmlPath);
        try {
            return DbManagerFactory.getDbManager(database, dbOptions);
        } catch (Exception e) {
            log.error("failed to create DbManager,please check your link information!");
            return null;
        }
    }
}
